#!/usr/bin/env julia

using Test
using TongLun

@testset "TongLun.jl Tests" begin
    
    @testset "Basic Value Types" begin
        @testset "None/Nothing Values" begin
            TongLun.export_fn("test.identity_none", x -> x)
            result = TongLun.call_fn("test.identity_none", nothing)
            @test result === nothing
        end
        
        @testset "Boolean Values" begin
            TongLun.export_fn("test.bool_not", x -> !x)
            TongLun.export_fn("test.bool_and", (x, y) -> x && y)
            TongLun.export_fn("test.bool_or", (x, y) -> x || y)
            
            @test TongLun.call_fn("test.bool_not", true) === false
            @test TongLun.call_fn("test.bool_not", false) === true
            @test TongLun.call_fn("test.bool_and", true, false) === false
            @test TongLun.call_fn("test.bool_and", true, true) === true
            @test TongLun.call_fn("test.bool_or", false, false) === false
            @test TongLun.call_fn("test.bool_or", true, false) === true
        end
        
        @testset "Integer Values" begin
            TongLun.export_fn("test.int_add", (x, y) -> x + y)
            TongLun.export_fn("test.int_multiply", (x, y) -> x * y)
            TongLun.export_fn("test.int_power", (x, y) -> x^y)
            TongLun.export_fn("test.int_abs", abs)
            
            @test TongLun.call_fn("test.int_add", 10, 20) === 30
            @test TongLun.call_fn("test.int_multiply", 6, 7) === 42
            @test TongLun.call_fn("test.int_power", 2, 10) === 1024
            @test TongLun.call_fn("test.int_abs", -5) === 5
            
            # Test large integers
            large_int = 9223372036854775807  # Near Int64 max
            @test TongLun.call_fn("test.int_abs", -large_int) === large_int
        end
        
        @testset "Float Values" begin
            TongLun.export_fn("test.float_add", (x, y) -> x + y)
            TongLun.export_fn("test.float_sqrt", sqrt)
            TongLun.export_fn("test.float_sin", sin)
            TongLun.export_fn("test.float_round", round)
            
            @test TongLun.call_fn("test.float_add", 3.14, 2.86) ≈ 6.0
            @test TongLun.call_fn("test.float_sqrt", 16.0) ≈ 4.0
            @test TongLun.call_fn("test.float_sin", π/2) ≈ 1.0
            @test TongLun.call_fn("test.float_round", 3.7) ≈ 4.0
            
            # Test special float values
            TongLun.export_fn("test.float_isnan", isnan)
            TongLun.export_fn("test.float_isinf", isinf)
            @test TongLun.call_fn("test.float_isnan", NaN) === true
            @test TongLun.call_fn("test.float_isinf", Inf) === true
        end
        
        @testset "Complex Values" begin
            TongLun.export_fn("test.complex_abs", abs)
            TongLun.export_fn("test.complex_conj", conj)
            TongLun.export_fn("test.complex_real", real)
            TongLun.export_fn("test.complex_imag", imag)
            TongLun.export_fn("test.identity", identity)
            
            # Test ComplexF64
            z1 = 3.0 + 4.0im
            @test TongLun.call_fn("test.complex_abs", z1) ≈ 5.0
            @test TongLun.call_fn("test.complex_conj", z1) ≈ (3.0 - 4.0im)
            @test TongLun.call_fn("test.complex_real", z1) ≈ 3.0
            @test TongLun.call_fn("test.complex_imag", z1) ≈ 4.0
            
            # Test ComplexF32 - comprehensive type preservation tests
            z32 = ComplexF32(1.5f0, 2.5f0)
            result_32 = TongLun.call_fn("test.complex_abs", z32)
            @test result_32 ≈ abs(z32)
            
            # Test that ComplexF32 type is preserved through roundtrip
            identity_result = TongLun.call_fn("test.identity", z32)
            @test identity_result isa ComplexF32
            @test identity_result === z32
            
            # Test ComplexF32 operations preserve type
            conj_result = TongLun.call_fn("test.complex_conj", z32)
            @test conj_result isa ComplexF32
            @test conj_result ≈ conj(z32)
            
            real_result = TongLun.call_fn("test.complex_real", z32)
            # Note: TongLun converts Float32 to Float64 since it doesn't support standalone Float32 values
            @test real_result isa Float64
            @test real_result ≈ Float64(real(z32))
            
            imag_result = TongLun.call_fn("test.complex_imag", z32)
            # Note: TongLun converts Float32 to Float64 since it doesn't support standalone Float32 values  
            @test imag_result isa Float64
            @test imag_result ≈ Float64(imag(z32))

            @info "complex tests passed"
        end
        
        @testset "String Values" begin
            TongLun.export_fn("test.string_length", length)
            TongLun.export_fn("test.string_upper", uppercase)
            TongLun.export_fn("test.string_concat", (x, y) -> string(x, y))
            TongLun.export_fn("test.string_reverse", reverse)
            TongLun.export_fn("test.string_contains", (s, sub) -> contains(s, sub))
            
            @test TongLun.call_fn("test.string_length", "Hello") === 5
            @test TongLun.call_fn("test.string_upper", "hello") == "HELLO"
            @test TongLun.call_fn("test.string_concat", "Hello", " World") == "Hello World"
            @test TongLun.call_fn("test.string_reverse", "abc") == "cba"
            @test TongLun.call_fn("test.string_contains", "hello world", "world") === true
            @test TongLun.call_fn("test.string_contains", "hello world", "xyz") === false
            
            # Test Unicode strings
            @test TongLun.call_fn("test.string_length", "你好世界") === 4
            @test TongLun.call_fn("test.string_reverse", "café") == "éfac"

            @info "string tests passed"
        end
    end
    
    @testset "Arrays" begin
        @testset "1D Arrays" begin
            TongLun.export_fn("test.array_sum", sum)
            TongLun.export_fn("test.array_length", length)
            TongLun.export_fn("test.array_mean", arr -> sum(arr) / length(arr))
            TongLun.export_fn("test.array_max", maximum)
            TongLun.export_fn("test.array_min", minimum)
            
            # Float64 array
            arr_f64 = [1.0, 2.0, 3.0, 4.0, 5.0]
            @test TongLun.call_fn("test.array_sum", arr_f64) ≈ 15.0
            @test TongLun.call_fn("test.array_length", arr_f64) === 5
            @test TongLun.call_fn("test.array_mean", arr_f64) ≈ 3.0
            @test TongLun.call_fn("test.array_max", arr_f64) ≈ 5.0
            @test TongLun.call_fn("test.array_min", arr_f64) ≈ 1.0
            
            # Int64 array
            arr_i64 = [10, 20, 30, 40, 50]
            @test TongLun.call_fn("test.array_sum", arr_i64) === 150
            @test TongLun.call_fn("test.array_length", arr_i64) === 5
            
            # Bool array
            arr_bool = [true, false, true, false]
            @test TongLun.call_fn("test.array_sum", arr_bool) === 2
            @test TongLun.call_fn("test.array_length", arr_bool) === 4
            
            # ComplexF32 array - test type preservation
            arr_c32 = ComplexF32[1.0f0+2.0f0im, 3.0f0+4.0f0im, 5.0f0+6.0f0im]
            @test TongLun.call_fn("test.array_length", arr_c32) === 3
            result_sum_c32 = TongLun.call_fn("test.array_sum", arr_c32)
            @test result_sum_c32 isa ComplexF32
            @test result_sum_c32 ≈ ComplexF32(9.0f0, 12.0f0)
            
            # ComplexF64 array for comparison
            arr_c64 = ComplexF64[1.0+2.0im, 3.0+4.0im, 5.0+6.0im]
            result_sum_c64 = TongLun.call_fn("test.array_sum", arr_c64)
            @test result_sum_c64 isa ComplexF64
            @test result_sum_c64 ≈ ComplexF64(9.0, 12.0)

            @info "1d array tests passed"
        end
        
        @testset "2D Arrays" begin
            @info "begin 2d array tests"
            TongLun.export_fn("test.matrix_size", size)
            TongLun.export_fn("test.matrix_sum", sum)
            TongLun.export_fn("test.matrix_transpose", transpose)
            
            # 2x3 matrix
            matrix = [1.0 2.0 3.0; 4.0 5.0 6.0]
            @test TongLun.call_fn("test.matrix_size", matrix) == (2, 3)
            @test TongLun.call_fn("test.matrix_sum", matrix) ≈ 21.0
            
            @info "begin transpose"
            result_transpose = TongLun.call_fn("test.matrix_transpose", matrix)

            @test size(result_transpose) == (3, 2)
            @test result_transpose[1, 1] ≈ 1.0
            @test result_transpose[3, 2] ≈ 6.0
        end
        
        @testset "3D Arrays" begin
            TongLun.export_fn("test.array3d_size", size)
            TongLun.export_fn("test.array3d_sum", sum)
            
            # 2x2x2 array
            arr3d = reshape(collect(1:8), 2, 2, 2)
            @test TongLun.call_fn("test.array3d_size", arr3d) == (2, 2, 2)
            @test TongLun.call_fn("test.array3d_sum", arr3d) === 36

            @info "3d array tests passed"
        end
        
        @testset "Array Operations" begin
            TongLun.export_fn("test.array_map_square", arr -> arr.^2)
            TongLun.export_fn("test.array_filter_positive", arr -> filter(x -> x > 0, arr))
            TongLun.export_fn("test.array_cumsum", cumsum)
            
            arr = [-2, -1, 0, 1, 2]
            
            squared = TongLun.call_fn("test.array_map_square", arr)
            @test squared == [4, 1, 0, 1, 4]
            
            positive = TongLun.call_fn("test.array_filter_positive", arr)
            @test positive == [1, 2]
            
            cumulative = TongLun.call_fn("test.array_cumsum", [1, 2, 3, 4])
            @test cumulative == [1, 3, 6, 10]

            @info "array tests passed"
        end
    end
    
    @testset "Tuples and Collections" begin
        @testset "Tuple Operations" begin
            TongLun.export_fn("test.tuple_length", length)
            TongLun.export_fn("test.tuple_first", first)
            TongLun.export_fn("test.tuple_last", last)
            TongLun.export_fn("test.make_tuple", (a, b, c) -> (a, b, c))
            TongLun.export_fn("test.tuple_reverse", reverse)
            
            test_tuple = (1, "hello", 3.14, true, [1, 2, 3])
            
            @test TongLun.call_fn("test.tuple_length", test_tuple) === 5
            @test TongLun.call_fn("test.tuple_first", test_tuple) === 1
            @test TongLun.call_fn("test.tuple_last", test_tuple) == [1, 2, 3]
            
            made_tuple = TongLun.call_fn("test.make_tuple", 42, "world", false)
            @test made_tuple == (42, "world", false)
            
            reversed_tuple = TongLun.call_fn("test.tuple_reverse", (1, 2, 3))
            @test reversed_tuple == (3, 2, 1)
        end
        
        @testset "Mixed Type Collections" begin
            TongLun.export_fn("test.mixed_sum", function(items)
                total = 0.0
                for item in items
                    if isa(item, Number)
                        total += item
                    elseif isa(item, AbstractString)
                        total += length(item)
                    elseif isa(item, AbstractArray)
                        total += length(item)
                    end
                end
                return total
            end)
            
            mixed_data = (10, "hello", [1, 2, 3, 4], 3.14)
            result = TongLun.call_fn("test.mixed_sum", mixed_data)
            @test result ≈ 22.14  # 10 + 5 + 4 + 3.14
        end
    end
    
    @testset "Mathematical Functions" begin
        @testset "Basic Math" begin
            TongLun.export_fn("test.factorial", factorial)
            TongLun.export_fn("test.gcd", gcd)
            TongLun.export_fn("test.lcm", lcm)
            TongLun.export_fn("test.fibonacci", function(n)
                if n <= 1
                    return n
                else
                    return TongLun.call_fn("test.fibonacci", n-1) + TongLun.call_fn("test.fibonacci", n-2)
                end
            end)
            
            @test TongLun.call_fn("test.factorial", 5) === 120
            @test TongLun.call_fn("test.gcd", 48, 18) === 6
            @test TongLun.call_fn("test.lcm", 12, 8) === 24
            @test TongLun.call_fn("test.fibonacci", 7) === 13
        end
        
        @testset "Trigonometric Functions" begin
            TongLun.export_fn("test.sin", sin)
            TongLun.export_fn("test.cos", cos)
            TongLun.export_fn("test.tan", tan)
            TongLun.export_fn("test.asin", asin)
            TongLun.export_fn("test.acos", acos)
            TongLun.export_fn("test.atan", atan)
            
            @test TongLun.call_fn("test.sin", 0.0) ≈ 0.0 atol=1e-15
            @test TongLun.call_fn("test.cos", 0.0) ≈ 1.0
            @test TongLun.call_fn("test.tan", π/4) ≈ 1.0
            @test TongLun.call_fn("test.asin", 0.5) ≈ π/6
            @test TongLun.call_fn("test.acos", 0.5) ≈ π/3
            @test TongLun.call_fn("test.atan", 1.0) ≈ π/4
        end
        
        @testset "Logarithmic Functions" begin
            TongLun.export_fn("test.log", log)
            TongLun.export_fn("test.log10", log10)
            TongLun.export_fn("test.log2", log2)
            TongLun.export_fn("test.exp", exp)
            TongLun.export_fn("test.exp10", exp10)
            TongLun.export_fn("test.exp2", exp2)
            
            @test TongLun.call_fn("test.log", Float64(ℯ)) == log(Float64(ℯ))
            @test TongLun.call_fn("test.log10", 100.0) == log10(100.0)
            @test TongLun.call_fn("test.log2", 8.0) == log2(8.0)
            @test TongLun.call_fn("test.exp", 1.0) == exp(1.0)
            @test TongLun.call_fn("test.exp10", 2.0) == 100.0
            @test TongLun.call_fn("test.exp2", 3.0) == 8.0
        end
    end
    
    @testset "Statistical Functions" begin
        using Statistics
        TongLun.export_fn("test.statistics", function(data)
            n = length(data)
            mean_val = sum(data) / n
            variance = sum((data .- mean_val).^2) / (n - 1)
            
            return (
                n,
                mean_val,
                variance,
                sqrt(variance),
                minimum(data),
                maximum(data),
                Statistics.median(data)
            )
        end)
        
        using Statistics
        test_data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
        (count, mean, variance, std_dev, min, max, median) = TongLun.call_fn("test.statistics", test_data)
        
        @test count === 10
        @test mean ≈ 5.5
        @test min ≈ 1.0
        @test max ≈ 10.0
        @test median ≈ 5.5
        @test std_dev > 0
    end
    
    @testset "Error Handling" begin
        @testset "Exception Propagation" begin
            TongLun.export_fn("test.divide_by_zero", x -> div(x, 0))
            TongLun.export_fn("test.array_bounds_error", arr -> arr[100])
            TongLun.export_fn("test.custom_error", x -> error("Custom error: $x"))
            TongLun.export_fn("test.domain_error", x -> sqrt(x))  # Will error for negative x
            
            @test_throws TongLun.TonglunError TongLun.call_fn("test.divide_by_zero", 5)
            @test_throws TongLun.TonglunError TongLun.call_fn("test.array_bounds_error", [1, 2, 3])
            
            # Test that error messages are preserved
            try
                TongLun.call_fn("test.custom_error", "test")
                @test false
            catch e
                @test isa(e, TongLun.TonglunError)
                @test contains(string(e), "Custom error: test")
            end
            
            # Test domain errors
            @test_throws TongLun.TonglunError TongLun.call_fn("test.domain_error", -1.0)
            @test TongLun.call_fn("test.domain_error", 4.0) ≈ 2.0  # Should work fine
        end
        
        @testset "Type Errors" begin
            TongLun.export_fn("test.string_specific", s -> uppercase(s))
            
            # This should work
            @test TongLun.call_fn("test.string_specific", "hello") == "HELLO"
            
            # This should throw an error (passing number to string function)
            @test_throws TongLun.TonglunError TongLun.call_fn("test.string_specific", 42)
        end
    end
    
    @testset "Memory Management" begin
        @testset "Large Array Handling" begin
            TongLun.export_fn("test.large_array_sum", sum)
            
            # Test with large array
            large_array = rand(10000)
            expected_sum = sum(large_array)
            result_sum = TongLun.call_fn("test.large_array_sum", large_array)
            @test result_sum ≈ expected_sum
        end
        
        @testset "Multiple Function Calls" begin
            TongLun.export_fn("test.increment", x -> x + 1)
            
            # Test many repeated calls
            value = 0
            for i in 1:1000
                value = TongLun.call_fn("test.increment", value)
            end
            @test value === 1000
        end
        
        @testset "Function Re-registration" begin
            # Test overriding a function
            TongLun.export_fn("test.changeable", x -> x * 2)
            @test TongLun.call_fn("test.changeable", 5) === 10
            
            # Override with different implementation
            TongLun.export_fn("test.changeable", x -> x * 3)
            @test TongLun.call_fn("test.changeable", 5) === 15

            @info "memory management tests passed"
        end
    end
    
    @testset "Cross-Language Integration" begin
        @testset "Julia.eval Function" begin
            # Test the built-in julia.eval function
            @test TongLun.call_fn("julia.eval", "2 + 3") === 5
            @test TongLun.call_fn("julia.eval", "sqrt(16)") ≈ 4.0
            @test TongLun.call_fn("julia.eval", "[1, 2, 3, 4]") == [1, 2, 3, 4]
            @test TongLun.call_fn("julia.eval", "\"Hello World\"") == "Hello World"
            
            # Test more complex expressions
            result = TongLun.call_fn("julia.eval", "sum([1, 2, 3, 4, 5])")
            @test result === 15
            
            # Test that eval can access Julia functions
            result = TongLun.call_fn("julia.eval", "factorial(5)")
            @test result === 120
        end
    end
    
    @testset "Performance and Edge Cases" begin
        @testset "Empty Collections" begin
            TongLun.export_fn("test.empty_array_length", length)
            TongLun.export_fn("test.empty_tuple_length", length)
            
            @test TongLun.call_fn("test.empty_array_length", Float64[]) === 0
            @test TongLun.call_fn("test.empty_tuple_length", ()) === 0
        end
        
        @testset "Single Element Collections" begin
            TongLun.export_fn("test.single_element_sum", sum)
            
            @test TongLun.call_fn("test.single_element_sum", [42.0]) ≈ 42.0
            @test TongLun.call_fn("test.single_element_sum", [100]) === 100
        end
        
        @testset "Extreme Values" begin
            TongLun.export_fn("test.identity", x -> x)
            
            # Test with very large numbers
            large_float = 1.0e100
            @test TongLun.call_fn("test.identity", large_float) ≈ large_float
            
            # Test with very small numbers
            small_float = 1.0e-100
            @test TongLun.call_fn("test.identity", small_float) ≈ small_float
        end
    end
    
    @testset "Type Conversion and Compatibility" begin
        @testset "Integer Type Promotion" begin
            TongLun.export_fn("test.add_ints", (x, y) -> x + y)
            
            # Test different integer types
            @test TongLun.call_fn("test.add_ints", Int32(10), Int32(20)) === 30
            @test TongLun.call_fn("test.add_ints", Int16(5), Int16(7)) === 12
            @test TongLun.call_fn("test.add_ints", UInt8(100), UInt8(50)) === 150
        end
        
        @testset "Float Type Handling" begin
            TongLun.export_fn("test.add_floats", (x, y) -> x + y)
            
            @test TongLun.call_fn("test.add_floats", Float32(1.5), Float32(2.5)) ≈ 4.0
            @test TongLun.call_fn("test.add_floats", 1.5, 2.5) ≈ 4.0
        end
    end
end 

include("quick_test.jl")
include("complex_test.jl")